#pragma once
#include "../Utils/PlatTypeUtil.h"
#include "Runnable.h"
#include <string>
#include <tchar.h>
#include <wtypes.h>


/**
 * Interface for runnable threads.
 *
 * This interface specifies the methods used to manage a thread's life cycle.
 */
class RunnableThread
{
	/** Index of TLS slot for RunnableThread pointer. */
	static uint32 sRunnableTlsSlot;

	HANDLE mThread;

	/**
	 * The thread entry point. Simply forwards the call on to the right
	 * thread main function
	 */
	static ::DWORD __stdcall _ThreadProc(LPVOID pThis)
	{
		return ((RunnableThread*)pThis)->Run();
	}

	static int TranslateThreadPriority(ThreadPriority threadPriority);

	/**
	 * The real thread entry point. It calls the Init/Run/Exit methods on
	 * the runnable object
	 */
	uint32 Run();

public:

	/** Gets a new Tls slot for storing the runnable thread pointer. */
	// static uint32 GetTlsSlot();

	/**
	 * Return false if this is an invalid TLS slot
	 * @param SlotIndex the TLS index to check
	 * @return true if this looks like a valid slot
	 */
	static bool IsValidTlsSlot(uint32 SlotIndex)
	{
		return SlotIndex != 0xFFFFFFFF;
	}

	/**
	 * Factory method to create a thread with the specified stack size and thread priority.
	 *
	 * @param InRunnable The runnable object to execute
	 * @param ThreadName Name of the thread
	 * @param InStackSize The size of the stack to create. 0 means use the current thread's stack size
	 * @param InThreadPri Tells the thread whether it needs to adjust its priority or not. Defaults to normal priority
	 * @return The newly created thread or nullptr if it failed
	 */
	static RunnableThread* Create(
		Runnable* runnable,
		const TCHAR* threadName,
		ThreadPriority threadPriority
		);

	/**
	 * Changes the thread priority of the currently running thread
	 *
	 * @param NewPriority The thread priority to change to
	 */
	virtual void SetThreadPriority(ThreadPriority threadPriority);

	/**
	 * Tells the thread to either pause execution or resume depending on the
	 * passed in value.
	 *
	 * @param bShouldPause Whether to pause the thread (true) or resume (false)
	 */
	virtual void Suspend( bool shouldPause = true );

	/**
	 * Tells the thread to exit. If the caller needs to know when the thread has exited, it should use the bShouldWait value.
	 * It's highly recommended not to kill the thread without waiting for it.
	 * Having a thread forcibly destroyed can cause leaks and deadlocks.
	 * 
	 * The kill method is calling Stop() on the runnable to kill the thread gracefully.
	 * 
	 * @param bShouldWait	If true, the call will wait infinitely for the thread to exit.					
	 * @return Always true
	 */
	virtual bool Kill( bool shouldWait = true );

	/** Halts the caller until this thread is has completed its work. */
	virtual void WaitForCompletion();

	/**
	 * Thread ID for this thread 
	 *
	 * @return ID that was set by CreateThread
	 * @see GetThreadName
	 */
	const uint32 GetThreadID() const
	{
		return mThreadId;
	}

	/**
	 * Retrieves the given name of the thread
	 *
	 * @return Name that was set by CreateThread
	 * @see GetThreadID
	 */
	const std::wstring& GetThreadName() const
	{
		return mThreadName;
	}

	/** Default constructor. */
	RunnableThread();

	/** Virtual destructor */
	virtual ~RunnableThread();

	/** Main thread constructor. */
	RunnableThread(Runnable* runnable, const TCHAR* threadName, ThreadPriority threadPriority, uint32 threadId);

protected:

	/**
	 * Creates the thread with the specified stack size and thread priority.
	 *
	 * @param InRunnable The runnable object to execute
	 * @param ThreadName Name of the thread
	 * @param InStackSize The size of the stack to create. 0 means use the current thread's stack size
	 * @param InThreadPri Tells the thread whether it needs to adjust its priority or not. Defaults to normal priority
	 * @return True if the thread and all of its initialization was successful, false otherwise
	 */
	virtual bool CreateInternal(Runnable* runnable, const TCHAR* threadName, ThreadPriority threadPriority);

	/** Stores this instance in the runnable thread TLS slot. */
	void SetTls();

	/** Deletes all FTlsAutoCleanup objects created for this thread. */
	void FreeTls();

	/**
	 * @return a runnable thread that is executing this runnable, if return value is nullptr, it means the running thread can be game thread or a thread created outside the runnable interface
	 */
	static RunnableThread* GetRunnableThread();

	/** The runnable object to execute on this thread. */
	Runnable* mRunnable;

	/** The priority to run the thread at. */
	ThreadPriority mThreadPriority;

	/** ID set during thread creation. */
	uint32 mThreadId;

	/** Holds the name of the thread. */
	std::wstring mThreadName;
};
